package com.oxxo.gallery.ftp;

import java.applet.Applet;
import java.awt.Graphics;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.oxxo.gallery.exception.MissingParamException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;

import org.apache.commons.net.io.CopyStreamEvent;
import org.apache.commons.net.io.CopyStreamListener;

/**
 * This Applet is used to upload files to an FTP Server. It acts like an FTP
 * client that receives the necessary parameters for the FTP server and the file
 * to upload. <br/>
 * Optionally it has the ability to redirect after successfully uploaded to
 * another webpage.
 * The applet uses the double-buffering technique to avoid flickering when
 * repainting the applet.
 * <a href="http://www.realapplets.com/tutorial/DoubleBuffering.html">
 * Double-buffering </a>
 * @author Ignacio Marmolejo
 * @version 0.0.1
 */
public class FtpUploadApplet extends Applet implements CopyStreamListener, FtpUploadErrorListener {

    private String localFile = null;
    private String remoteFile = null;
    private String ftpDir = null;
    private String ftpAddress = null;
    private String ftpUsername = null;
    private String ftpPassword = null;
    private String redirectUrl = null;
    private Logger logger = null;
    private Thread uploadThread = null;
    private FtpUploadThread ftpThread = null;
    private boolean fileUploaded = false;

    private Graphics bufferGraphics;
    private Image offscreen;
    private Dimension dim;
    private int widthTransfered = 0;
    private long totalBytesTransferred = 0;
    private long fileSize = 0;
    private double transferedPercentage = 0.0;

    /**
     * Initialization method that will be called after the applet is loaded into
     * the browser.
     */
    @Override
    public void init() {
        logger = Logger.getLogger(FtpUploadApplet.class.getName());
        logger.log(Level.INFO, "init");

        try {
            initParameters();
        } catch (MissingParamException mpe) {
            logger.log(Level.SEVERE, mpe.toString());
            System.exit(1);
        }
        dim = getSize();
        offscreen = createImage(dim.width, dim.height);
        bufferGraphics = offscreen.getGraphics();
        ftpThread = new FtpUploadThread(localFile, remoteFile, ftpAddress,
                ftpUsername, ftpPassword, ftpDir);
        ftpThread.setListener(this);
        ftpThread.addErrorListener(this);
        uploadThread = new Thread(ftpThread);
    }

    /**
     * Set the necessary parameters for the application that are passed as
     * params in the html source
     */
    private void initParameters() throws MissingParamException {
        if ((localFile = getParameter("localFile")) == null) {
            throw new MissingParamException("Missing the localFile param");
        }

        if ((remoteFile = getParameter("remoteFile")) == null) {
            throw new MissingParamException("Missing the remoteFile param");
        }

        if ((ftpDir = getParameter("ftpDir")) == null) {
            throw new MissingParamException("Missing ftpDir param");
        }

        if ((ftpAddress = getParameter("ftpAddress")) == null) {
            throw new MissingParamException("Missing ftpAddress param");
        }

        if ((ftpUsername = getParameter("ftpUsername")) == null) {
            throw new MissingParamException("Missing ftpUsername param");
        }

        if ((ftpPassword = getParameter("ftpPassword")) == null) {
            throw new MissingParamException("Missing ftpPassword param");
        }

        if ((redirectUrl = getParameter("redirectUrl")) == null) {
            // This param is optional
        }

        logger.log(Level.INFO, "Local File: "  + localFile);
        logger.log(Level.INFO, "Remote File: "  + remoteFile);
        logger.log(Level.INFO, "FTP Dir: "  + ftpDir);
        logger.log(Level.INFO, "redirect URL: "  + redirectUrl);
    }

    /**
     * Shows the percentage transfered by the thread and shows a graphic
     * that updates while the bytes are transfered.<br>
     * These are painted in an Image. Then the image is updated in the applet.
     * This is technique called: 
     * <a href="http://www.realapplets.com/tutorial/DoubleBuffering.html">
     * Double-buffering </a>
     * @param g The graphic where we are going to paint
     */
    @Override
    public void paint(Graphics g) {
        int posX = 0;
        int posY = 0;
        bufferGraphics.clearRect(0, 0, dim.width, dim.height);
        bufferGraphics.drawString("Uploading file to ftp server!", posX, posY += 20);
        bufferGraphics.drawString("Transfered: " + (int)transferedPercentage
                + "%", posX, posY += 20);
        // Let's see if we have errors and iterate the list to show error messages to the user
        if (ftpThread.hasErrors()) {
          bufferGraphics.setColor(Color.RED);
          for (String error : ftpThread.getErrorList())
            bufferGraphics.drawString(error, posX, posY += 20);
        }
        bufferGraphics.setColor(Color.BLUE);
        bufferGraphics.fillRect(posX, posY += 20, widthTransfered, 20);
        
        g.drawImage(offscreen, 0, 0, this);
        
        if (fileUploaded) {
            if (redirectUrl != null) {
                try {
                    URL url = new URL(redirectUrl);
                    this.getAppletContext().showDocument(url);
                } catch (MalformedURLException mue) {
                    logger.log(Level.WARNING, null, mue);
                }
            }
        }
    }

    /**
     * Event that updates the bytes transfered by the ftp client.
     * @param totalBytesTransferred Total number of bytes transfered
     * @param bytesTransferred Number of byte transfered during these event
     * @param streamSize The size of the buffer being transfered
     */
    public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize) {
        widthTransfered = (int)((totalBytesTransferred * dim.width) / streamSize);
        fileSize = streamSize;
        this.totalBytesTransferred = totalBytesTransferred;
        if(this.totalBytesTransferred == streamSize) {
            fileUploaded = true;
        }
        transferedPercentage = ((double)totalBytesTransferred / (double)streamSize) * 100;
        repaint();
    }

    public void bytesTransferred(CopyStreamEvent cse) {
        
    }

    @Override
    public void start() {
        logger.log(Level.INFO, "start");
        uploadThread.start();
    }

    @Override
    public void stop() {
        logger.log(Level.INFO, "stop");
        try {
            uploadThread.join();
        } catch (InterruptedException ex) {
            logger.log(Level.SEVERE, null, ex);
        }

    }


    /**
     * Allow us to use Double-buffering to avoid applet to flicker
     * <a href="http://www.realapplets.com/tutorial/DoubleBuffering.html">
     * Double-buffering </a>
     * @param g The graphic to update
     */
    @Override
    public void update(Graphics g) {
        paint(g);
    }

    @Override
    public void destroy() {
        logger.log(Level.INFO, "destroy");
    }

    public void TransferError(FtpUploadEvent event)
    {
      repaint();      
    }

}
